package com.dayuanit.dy8.atm.springbootatm.service;

import com.dayuanit.dy8.atm.springbootatm.dto.CardDTO;
import com.dayuanit.dy8.atm.springbootatm.dto.FlowDTO;
import com.dayuanit.dy8.atm.springbootatm.entity.Card;
import com.dayuanit.dy8.atm.springbootatm.entity.Flow;
import com.dayuanit.dy8.atm.springbootatm.entity.Transfer;
import com.dayuanit.dy8.atm.springbootatm.exception.BizException;
import com.dayuanit.dy8.atm.springbootatm.holder.PageHolder;
import com.dayuanit.dy8.atm.springbootatm.mapper.CardMapper;
import com.dayuanit.dy8.atm.springbootatm.mapper.FlowMapper;
import com.dayuanit.dy8.atm.springbootatm.mapper.TransferMapper;
import com.dayuanit.dy8.atm.springbootatm.util.BankCardUtils;
import com.dayuanit.dy8.atm.springbootatm.util.DateUtils;
import com.dayuanit.dy8.atm.springbootatm.util.MoneyUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class BankCardService {

    @Autowired
    private CardMapper cardMapper;

    @Autowired
    private FlowMapper flowMapper;

    @Autowired
    private TransferMapper transferMapper;

    @Autowired
    private TransferService transferService;

    public void openAccount(String pwd, String confirmPwd, int userId) {

        if (StringUtils.isBlank(pwd) ||
                StringUtils.isBlank(confirmPwd)) {
            throw new BizException("密码不能为空");
        }

        if (!pwd.equals(confirmPwd)) {
            throw new BizException("两次密码不相等");
        }

        //  生成卡号
        final String cardNum = BankCardUtils.createCardNum();

        //  校验卡号是否存在
        final int count = cardMapper.countCardByNum(cardNum);
        if (count > 0) {
            throw new BizException("卡号已存在,请重新开户");
        }

        // 保存银行卡
        final Card card = new Card();
        card.setBalance("0.00");
        card.setCardNum(cardNum);
        card.setCreateTime(new Date());
        card.setModifyTime(new Date());
        card.setPwd(pwd);
        //银行卡状态 1-正常  2-注销
        card.setStatus((byte)1);
        card.setUserId(userId);
        card.setVersion(0);

        int rows = cardMapper.insert(card);
        if (1 != rows) {
            throw new BizException("开户失败");
        }
    }

    /**
     * 悲观锁存钱
     */
    @Transactional(rollbackFor = Exception.class)
    public void depositBad(String cardNum, String moeny, int userId) {
        if (StringUtils.isBlank(moeny)) {
            throw new BizException("请输入存款数额");
        }

        if (StringUtils.isBlank(cardNum)) {
            throw new BizException("请输入银行卡号");
        }

        final Double aDouble = Double.valueOf(moeny);
        if (aDouble <= 0) {
            throw new BizException("存款金额必须大于0");
        }

        // 悲观锁对银行卡加钱
        final Card card = cardMapper.getCard4Lock(cardNum);
        if (null == card) {
            throw new BizException("卡号不存在");
        }
        card.setBalance(MoneyUtils.plus( card.getBalance(), moeny));

        //通过乐观锁修改金额
        int rows = cardMapper.changeBalance(card.getId(), card.getVersion(), card.getBalance());
        if (1 != rows) {
            throw new BizException("存款失败");
        }

        // int i = 10 / 0;

        //增加流水
        final Flow flow = new Flow();
        flow.setAmount(moeny);
        flow.setCardId(card.getId());
        flow.setCardNum(card.getCardNum());
        flow.setCreateTime(new Date());
        flow.setFlowDesc("存款");
        //流水类型 1-存钱 2-取钱 3-转账支出 4-转账收入
        flow.setFlowType((byte)1);
        flow.setUserId(userId);

        rows = flowMapper.insert(flow);
        if (1 != rows) {
            throw new BizException("添加流水失败");
        }

    }

    /**
     * 默认情况下 rollback只会对非检查型异常起作用 乐观锁存钱
     * @param cardNum
     * @param moeny
     * @param userId
     * @throws IOException
     */
    @Transactional(rollbackFor = Exception.class)
    public void deposit(String cardNum, String moeny, int userId) {
        if (StringUtils.isBlank(moeny)) {
            throw new BizException("请输入存款数额");
        }

        if (StringUtils.isBlank(cardNum)) {
            throw new BizException("请输入银行卡号");
        }

        final Double aDouble = Double.valueOf(moeny);
        if (aDouble <= 0) {
            throw new BizException("存款金额必须大于0");
        }

        // 对银行卡加钱
        final Card card = cardMapper.getCard(cardNum);
        if (null == card) {
            throw new BizException("卡号不存在");
        }
        card.setBalance(MoneyUtils.plus( card.getBalance(), moeny));

        //通过乐观锁修改金额
        int rows = cardMapper.changeBalance(card.getId(), card.getVersion(), card.getBalance());
        if (1 != rows) {
            throw new BizException("存款失败");
        }

       // int i = 10 / 0;
//        if (1 == 1) {
//            throw new IOException("xxxx");
//        }

        //增加流水
        final Flow flow = new Flow();
        flow.setAmount(moeny);
        flow.setCardId(card.getId());
        flow.setCardNum(card.getCardNum());
        flow.setCreateTime(new Date());
        flow.setFlowDesc("存款");
        //流水类型 1-存钱 2-取钱 3-转账支出 4-转账收入
        flow.setFlowType((byte)1);
        flow.setUserId(userId);

        rows = flowMapper.insert(flow);
        if (1 != rows) {
            throw new BizException("添加流水失败");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void draw(String cardNum, String amount, String pwd, int userId) {
        // 校验参数 是否必填 特定参数的校验 金额正负  卡号 密码
        if (StringUtils.isBlank(cardNum) || StringUtils.isBlank(amount) || StringUtils.isBlank(pwd)) {
            throw new BizException("请输入必填参数");
        }

        double money = Double.valueOf(amount);
        if (money <= 0) {
            throw new BizException("金额必须大于零");
        }

        final Card card = cardMapper.getCard(cardNum);
        if (null == card) {
            throw new BizException("银行卡不存在或者密码错误");
        }

        if (!card.getPwd().equals(pwd)) {
            throw new BizException("银行卡不存在或者密码错误");
        }

        // 减去金额  注意 余额是否足够
        card.setBalance(MoneyUtils.sub(card.getBalance(), amount));
        if (Double.valueOf(card.getBalance()) < 0) {
            throw new BizException("余额不足，请充值");
        }

        //数据库更新余额 乐观锁
        int rows = cardMapper.changeBalance(card.getId(), card.getVersion(), card.getBalance());
        if (1 != rows) {
            throw new BizException("取款失败");
        }

        // 添加流水
        final Flow flow = new Flow();
        flow.setAmount(amount);
        flow.setCardId(card.getId());
        flow.setCardNum(card.getCardNum());
        flow.setCreateTime(new Date());
        flow.setFlowDesc("取款");
        //流水类型 1-存钱 2-取钱 3-转账支出 4-转账收入
        flow.setFlowType((byte)2);
        flow.setUserId(userId);

        rows = flowMapper.insert(flow);
        if (1 != rows) {
            throw new BizException("添加流水失败");
        }
    }

    public PageHolder listFlows(String cardNum, String pwd, int userId, int currentPage) {
        //参数校验
        if (StringUtils.isBlank(cardNum) || StringUtils.isBlank(pwd)) {
            throw new BizException("有必填参数不能为空");
        }

        final Card card = cardMapper.getCard(cardNum);
        if (null == card) {
            throw new BizException("银行卡不存在或者密码错误");
        }

        if (!card.getPwd().equals(pwd)) {
            throw new BizException("银行卡不存在或者密码错误");
        }

        if (card.getUserId() != userId) {
            throw new BizException("银行卡不属于你");
        }

        //查询总条数
        int totalElement = flowMapper.countFlows(userId, cardNum);

        PageHolder pageHolder = PageHolder.build(currentPage, totalElement);

        //根据银行卡号查询流水
        final List<Flow> flows = flowMapper.listFlows(userId, cardNum, pageHolder.getOffset(), PageHolder.prePageNum);//3
        final List<FlowDTO> dtoList = new ArrayList<>();//3

        for (Flow flow : flows) {
            FlowDTO flowDTO = new FlowDTO();
            dtoList.add(flowDTO);

            flowDTO.setDesc(flow.getFlowDesc());
            flowDTO.setAmount(flow.getAmount());
            flowDTO.setCardNum(flow.getCardNum());
            flowDTO.setCreateTime(DateUtils.dateToString(flow.getCreateTime()));
        }

        pageHolder.setData(dtoList);
        return pageHolder;
    }

    @Transactional(rollbackFor = Exception.class)
    public void transfer(String outCardNum, String inCardNum, String pwd, String amount, int userId) {
        // 相关参数校验 必填 卡号是否存在 密码是否正确 卡是否属于你 金额校验 余额检查
        if (StringUtils.isBlank(outCardNum) ||
                StringUtils.isBlank(inCardNum) ||
                StringUtils.isBlank(pwd) ||
                StringUtils.isBlank(amount)) {
            throw new BizException("有必填参数不能为空");
        }

        double money = Double.parseDouble(amount);
        if (money <= 0) {
            throw new BizException("转账金额必须大于零");
        }

        final Card outCard = cardMapper.getCard(outCardNum);
        if (outCard == null) {
            throw new BizException("卡号不合法或者密码错误");
        }

        if (!outCard.getPwd().equals(pwd)) {
            throw new BizException("卡号不合法或者密码错误");
        }

        //余额校验
        if (Double.parseDouble(MoneyUtils.sub(outCard.getBalance(), amount)) < 0) {
            throw new BizException("余额不足");
        }

        final Card inCard = cardMapper.getCard(inCardNum);
        if (null == inCard) {
            throw new BizException("转入卡号不合法");
        }

        // 转出 和 流水
        outCard.setBalance(MoneyUtils.sub(outCard.getBalance(), amount));
        int rows = cardMapper.changeBalance(outCard.getId(), outCard.getVersion(), outCard.getBalance());
        if (1 != rows) {
            throw new BizException("转账失败");
        }

        Flow flow = new Flow();
        flow.setAmount(amount);
        flow.setCardId(outCard.getId());
        flow.setCardNum(outCard.getCardNum());
        flow.setCreateTime(new Date());
        flow.setFlowDesc("转账支出");
        //流水类型 1-存钱 2-取钱 3-转账支出 4-转账收入
        flow.setFlowType((byte)3);
        flow.setUserId(userId);

        rows = flowMapper.insert(flow);
        if (1 != rows) {
            throw new BizException("添加流水失败");
        }

        //TODO 转入 和 流水
        inCard.setBalance(MoneyUtils.plus(inCard.getBalance(), amount));
        rows = cardMapper.changeBalance(inCard.getId(), inCard.getVersion(), inCard.getBalance());
        if (1 != rows) {
            throw new BizException("转账失败");
        }

        flow = new Flow();
        flow.setAmount(amount);
        flow.setCardId(inCard.getId());
        flow.setCardNum(inCard.getCardNum());
        flow.setCreateTime(new Date());
        flow.setFlowDesc("转账收入");
        //流水类型 1-存钱 2-取钱 3-转账支出 4-转账收入
        flow.setFlowType((byte)4);
        flow.setUserId(inCard.getUserId());

        rows = flowMapper.insert(flow);
        if (1 != rows) {
            throw new BizException("添加流水失败");
        }
    }

    public List<CardDTO> listCards(int userId) {
        final List<Card> cards = cardMapper.listCards(userId);
//        final List<CardDTO> dtoList = new ArrayList<>();
        final List<CardDTO> dtoList = new ArrayList<>(cards.size());

        for (Card card : cards) {
            CardDTO dto = new CardDTO();
            dtoList.add(dto);

            dto.setAmount(card.getBalance());
            dto.setCardId(card.getId());
            dto.setCardNum(card.getCardNum());
//            String f = card.getCardNum().substring(0,1);
//            String s = card.getCardNum().substring(card.getCardNum().length() - 1, card.getCardNum().length());
//            dto.setCardNum(f + "***" + s);//这里可以对银行卡号处理
        }

        return dtoList;
    }

    public void transferDelayTmp(String outCardNum, String inCardNum, String pwd, String amount, int userId) {
        //如此这般调用 是木有事务的 注意！！！  解决方法：可以将transferDelay方法移到另外一个service中，然后注入到本类中，即可开启事物。
        transferDelay(outCardNum, inCardNum, pwd, amount, userId);
    }

    /**
     * 延迟转账
     */
    @Transactional(rollbackFor = Exception.class)
    public void transferDelay(String outCardNum, String inCardNum, String pwd, String amount, int userId) {
        if (StringUtils.isBlank(outCardNum) ||
                StringUtils.isBlank(inCardNum) ||
                StringUtils.isBlank(pwd) ||
                StringUtils.isBlank(amount)) {
            throw new BizException("有必填参数不能为空");
        }

        double money = Double.parseDouble(amount);
        if (money <= 0) {
            throw new BizException("转账金额必须大于零");
        }

        final Card outCard = cardMapper.getCard(outCardNum);
        if (outCard == null) {
            throw new BizException("卡号不合法或者密码错误");
        }

        if (!outCard.getPwd().equals(pwd)) {
            throw new BizException("卡号不合法或者密码错误");
        }

        //余额校验
        if (Double.parseDouble(MoneyUtils.sub(outCard.getBalance(), amount)) < 0) {
            throw new BizException("余额不足");
        }

        final Card inCard = cardMapper.getCard(inCardNum);
        if (null == inCard) {
            throw new BizException("转入卡号不合法");
        }

        // 转出 和 流水
        outCard.setBalance(MoneyUtils.sub(outCard.getBalance(), amount));
        int rows = cardMapper.changeBalance(outCard.getId(), outCard.getVersion(), outCard.getBalance());
        if (1 != rows) {
            throw new BizException("转账失败");
        }

        Flow flow = new Flow();
        flow.setAmount(amount);
        flow.setCardId(outCard.getId());
        flow.setCardNum(outCard.getCardNum());
        flow.setCreateTime(new Date());
        flow.setFlowDesc("转账支出");
        //流水类型 1-存钱 2-取钱 3-转账支出 4-转账收入
        flow.setFlowType((byte)3);
        flow.setUserId(userId);

        rows = flowMapper.insert(flow);
        if (1 != rows) {
            throw new BizException("添加流水失败");
        }

//        int i = 10 / 0;

        // 记录转账订单
        final Transfer transfer = new Transfer();
        transfer.setAmount(amount);
        transfer.setCreateTime(new Date());
        transfer.setInCardNum(inCard.getCardNum());
        transfer.setModifyTime(new Date());
        transfer.setOutCardNum(outCard.getCardNum());
        transfer.setStatus(0);
        rows = transferMapper.insert(transfer);
        if (1 != rows) {
            throw new BizException("转账失败");
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public void transferIn2(Transfer transfer) {
        try {
            // 转入人加钱和流水
            Card inCard = cardMapper.getCard(transfer.getInCardNum());
            inCard.setBalance(MoneyUtils.plus(inCard.getBalance(), transfer.getAmount()));
            int rows = cardMapper.changeBalance(inCard.getId(), inCard.getVersion(), inCard.getBalance());
            if (1 != rows) {
                throw new BizException("转账失败");
            }

            Flow flow = new Flow();
            flow.setAmount(transfer.getAmount());
            flow.setCardId(inCard.getId());
            flow.setCardNum(inCard.getCardNum());
            flow.setCreateTime(new Date());
            flow.setFlowDesc("转账收入");
            //流水类型 1-存钱 2-取钱 3-转账支出 4-转账收入
            flow.setFlowType((byte)4);
            flow.setUserId(inCard.getUserId());

            rows = flowMapper.insert(flow);
            if (1 != rows) {
                throw new BizException("添加流水失败");
            }



            //修改转账记录的状态为成功 失败改为失败
            rows = transferMapper.updateStatus(transfer.getId(), transfer.getStatus(), 1);
            if (1 != rows) {
                throw new BizException("转账失败");
            }

//            int i = 10 / 0;

        } catch (Exception e) {
            e.printStackTrace();
            // 修改转账订单状态为待回滚
            System.out.println(">>>>即将修改订单状态为2");
            transferService.changeTransferStatus(transfer, 2);
            throw new BizException("转账失败");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void transferIn(Transfer transfer) {
        // 转入人加钱和流水
        Card inCard = cardMapper.getCard(transfer.getInCardNum());
        inCard.setBalance(MoneyUtils.plus(inCard.getBalance(), transfer.getAmount()));
        int rows = cardMapper.changeBalance(inCard.getId(), inCard.getVersion(), inCard.getBalance());
        if (1 != rows) {
            throw new BizException("转账失败");
        }

        Flow flow = new Flow();
        flow.setAmount(transfer.getAmount());
        flow.setCardId(inCard.getId());
        flow.setCardNum(inCard.getCardNum());
        flow.setCreateTime(new Date());
        flow.setFlowDesc("转账收入");
        //流水类型 1-存钱 2-取钱 3-转账支出 4-转账收入
        flow.setFlowType((byte)4);
        flow.setUserId(inCard.getUserId());

        rows = flowMapper.insert(flow);
        if (1 != rows) {
            throw new BizException("添加流水失败");
        }

//        int i = 10 / 0;

        //修改转账记录的状态为成功 失败改为失败
        rows = transferMapper.updateStatus(transfer.getId(), transfer.getStatus(), 1);
        if (1 != rows) {
            throw new BizException("转账失败");
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public void transferCancel(Transfer transfer) {
        // 给转账人加钱 和 流水
        Card inCard = cardMapper.getCard(transfer.getOutCardNum());
        inCard.setBalance(MoneyUtils.plus(inCard.getBalance(), transfer.getAmount()));
        int rows = cardMapper.changeBalance(inCard.getId(), inCard.getVersion(), inCard.getBalance());
        if (1 != rows) {
            throw new BizException("转账失败");
        }

        Flow flow = new Flow();
        flow.setAmount(transfer.getAmount());
        flow.setCardId(inCard.getId());
        flow.setCardNum(inCard.getCardNum());
        flow.setCreateTime(new Date());
        flow.setFlowDesc("转账取消");
        //流水类型 1-存钱 2-取钱 3-转账支出 4-转账收入 5-转账取消
        flow.setFlowType((byte)5);
        flow.setUserId(inCard.getUserId());

        rows = flowMapper.insert(flow);
        if (1 != rows) {
            throw new BizException("添加流水失败");
        }

        //修改转账记录为取消
        rows = transferMapper.updateStatus(transfer.getId(), transfer.getStatus(), 3);
        if (1 != rows) {
            throw new BizException("转账取消失败");
        }

    }

    public static void main(String[] args) {
        String cardCum = "12345";
        String f = cardCum.substring(0,1);
        String s = cardCum.substring(cardCum.length() - 1, cardCum.length());
        System.out.println(f);
        System.out.println(s);
    }
}
